using System;
using System.IO;
using System.Text;
using System.Xml.Serialization;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;

namespace Azure.Infrastructure.Azure
{
    public class AzureQueue<T> : IQueue<T> where T : IMessage
    {
        private readonly CloudStorageAccount _storageAccount;
        private readonly CloudQueueClient _cloudQueueClient;
        private readonly CloudQueue _queue;
        private readonly XmlSerializer _serializer;

        public AzureQueue(CloudStorageAccount storageAccount)
        {
            _storageAccount = storageAccount;
            _cloudQueueClient = _storageAccount.CreateCloudQueueClient();
            _queue = _cloudQueueClient.GetQueueReference(For<T>.Queue);
            _serializer = new XmlSerializer(typeof(T));
        }

        public void Enqueue(T item)
        {
            _queue.CreateIfNotExist();

            var message = CreateMessage(item);
            _queue.AddMessage(message);
        }

        public QueuedMessage<T> Dequeue(TimeSpan timeout)
        {
            _queue.CreateIfNotExist();

            var message = _queue.GetMessage();
            var body = ParseBody(message.AsString);
            var result = new QueuedMessage<T>(message.ExpirationTime, message.Id, message.InsertionTime, body);
            return result;
        }

        protected CloudQueueMessage CreateMessage(T item)
        {
            var body = ParseBody(item);
            var result = new CloudQueueMessage(body);
            return result;
        }

        protected T ParseBody(string body)
        {
            T result;

            using (var reader = new StringReader(body))
            {
                result = (T) _serializer.Deserialize(reader);
                reader.Close();
            }

            return result;
        }

        protected string ParseBody(T item)
        {
            var result = new StringBuilder();
            using (var writer = new StringWriter(result))
            {
                try
                {
                    _serializer.Serialize(writer, item);
                }
                catch (Exception)
                {
                    throw;
                }
            }

            return result.ToString();
        } 
    }
}